package com.luo.chengrui.module.sa.service.datapermission;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
import com.luo.chengrui.module.sa.controller.admin.datapermission.vo.DataPermissionDetailCreateReqVO;
import com.luo.chengrui.module.sa.controller.admin.datapermission.vo.DataPermissionDetailExportReqVO;
import com.luo.chengrui.module.sa.controller.admin.datapermission.vo.DataPermissionDetailPageReqVO;
import com.luo.chengrui.module.sa.controller.admin.datapermission.vo.DataPermissionDetailUpdateReqVO;
import com.luo.chengrui.module.sa.convert.datapermission.DataPermissionDetailConvert;
import com.luo.chengrui.module.sa.dal.dataobject.datapermission.DataPermissionConfigDO;
import com.luo.chengrui.module.sa.dal.dataobject.datapermission.DataPermissionDefineDO;
import com.luo.chengrui.module.sa.dal.dataobject.datapermission.DataPermissionDetailDO;
import com.luo.chengrui.module.sa.dal.dataobject.datapermission.DataPermissionItemDO;
import com.luo.chengrui.module.sa.dal.mysql.datapermission.DataPermissionConfigMapper;
import com.luo.chengrui.module.sa.dal.mysql.datapermission.DataPermissionDefineMapper;
import com.luo.chengrui.module.sa.dal.mysql.datapermission.DataPermissionDetailMapper;
import com.luo.chengrui.module.sa.dal.mysql.datapermission.DataPermissionItemMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import com.luo.chengrui.module.sa.dal.redis.RedisKeyConstants;
import com.luo.chengrui.module.sa.enums.DataPermissionScopeEnum;
import com.luo.chengrui.module.sa.enums.OrgKindEnum;
import com.luo.chengrui.module.sa.service.datapermission.DataPermissionDetailService;
import com.luo.chengrui.module.sa.service.org.OrgService;
import com.luo.chengrui.module.sa.service.person.PersonService;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.luo.chengrui.framework.sadatapermission.core.annotation.SaDataPermission;
import com.luo.chengrui.module.sa.api.permission.dto.UserDataPermissionRespDTO;
import com.luo.chengrui.module.sa.enums.LikeOrInEnum;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

import static com.luo.chengrui.module.sa.enums.ErrorCodeConstants.*;


/**
 * 数据权限明细 Service 实现类
 *
 * @author 诚睿
 */
@Service
@Validated
public class DataPermissionDetailServiceImpl implements DataPermissionDetailService {

    @Resource
    private DataPermissionDetailMapper dataPermissionDetailMapper;
    @Resource
    private DataPermissionItemMapper dataPermissionItemMapper;
    @Resource
    private DataPermissionConfigMapper dataPermissionConfigMapper;
    @Resource
    private DataPermissionDefineMapper dataPermissionDefineMapper;
    @Resource
    private PersonService personService;
    @Resource
    private OrgService orgService;

    private DataPermissionDetailServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

    /**
     * 用户数据权限缓存
     */
    private final LoadingCache<KeyValue<Long, String>, UserDataPermissionRespDTO> userDataPermissionCache = CacheUtils.buildAsyncReloadingCache(
            Duration.ofMinutes(3L), // 过期时间 3 分钟
            new CacheLoader<KeyValue<Long, String>, UserDataPermissionRespDTO>() {
                @Override
                public UserDataPermissionRespDTO load(KeyValue<Long, String> key) throws Exception {
                    return getSelf().getUserDataPermissionByCode2(key.getKey(), key.getValue());
                }
            });

    @Override
    public String createDataPermissionDetail(DataPermissionDetailCreateReqVO createReqVO) {
        // 插入
        DataPermissionDetailDO dataPermissionDetail = DataPermissionDetailConvert.INSTANCE.convert(createReqVO);
        dataPermissionDetailMapper.insert(dataPermissionDetail);
        // 返回
        return dataPermissionDetail.getId();
    }

    @Override
    public void updateDataPermissionDetail(DataPermissionDetailUpdateReqVO updateReqVO) {
        // 更新
        DataPermissionDetailDO updateObj = DataPermissionDetailConvert.INSTANCE.convert(updateReqVO);
        dataPermissionDetailMapper.updateById(updateObj);
    }

    @Override
    public void deleteDataPermissionDetail(String id) {
        // 删除
        dataPermissionDetailMapper.deleteById(id);
    }

    @Override
    public DataPermissionDetailDO getDataPermissionDetail(String id) {
        return dataPermissionDetailMapper.selectById(id);
    }

    @Override
    public List<DataPermissionDetailDO> getDataPermissionDetailList(Collection<String> ids) {
        return dataPermissionDetailMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<DataPermissionDetailDO> getDataPermissionDetailPage(DataPermissionDetailPageReqVO pageReqVO) {
        return dataPermissionDetailMapper.selectPage(pageReqVO);
    }

    @Override
    public List<DataPermissionDetailDO> getDataPermissionDetailList(DataPermissionDetailExportReqVO exportReqVO) {
        return dataPermissionDetailMapper.selectList(exportReqVO);
    }

    /**
     * 查询用户数据权限
     *
     * @param userId         system_user表中用户ID
     * @param permissionCode 权限项明细编码
     * @return
     */
    @SaDataPermission(enable = false) //必须设置不添加权限，否则会转圈
    public UserDataPermissionRespDTO getUserDataPermissionByCode2(Long userId, String permissionCode) {
        // 如果为空，说明无权限
        if (StrUtil.isEmpty(permissionCode)) {
            throw exception(DATA_PERMISSION_ITEM_SUB_CODE_NOT_EXISTS);
        }


        DataPermissionItemDO permissionItemDO = dataPermissionItemMapper.selectBySubCode(new DataPermissionItemDO().setCode(permissionCode));
        if (permissionItemDO == null) {
            permissionItemDO = dataPermissionItemMapper.selectOne(DataPermissionItemDO::getCode, permissionCode);
            if (permissionItemDO == null) {
                throw exception(DATA_PERMISSION_ITEM_SUB_NOT_EXISTS);
            }
        }
        List<String> personFullIds = personService.getPersonFullIdsBySystemUserIdFromCache(userId);
        if (CollUtil.isEmpty(personFullIds)) {
            // 没有有效岗位，说明无权限
            return null;
        }
        DataPermissionDefineDO dataPermissionDefineDO = dataPermissionDefineMapper.selectById(permissionItemDO.getDefineId());
        List<DataPermissionConfigDO> permissionConfigList = dataPermissionConfigMapper.selectUserPermissionList(permissionItemDO.getId(), personFullIds);

        if (CollUtil.isEmpty(permissionConfigList)) {
            // 没有配置任何权限，说明无权限
            return null;
        }
        UserDataPermissionRespDTO dataPermissionRespDTO = new UserDataPermissionRespDTO();
        dataPermissionRespDTO.setPermissionItemSubCode(permissionCode);
        dataPermissionRespDTO.setPermissionItemId(permissionItemDO.getId());
        dataPermissionRespDTO.setDefineId(permissionItemDO.getDefineId());
        dataPermissionRespDTO.setTableName(dataPermissionDefineDO.getTableName());
        dataPermissionRespDTO.setForeignKeyName(dataPermissionDefineDO.getForeignKeyName());
        dataPermissionRespDTO.setPrimaryKeyName(dataPermissionDefineDO.getPrimaryKeyName());
        dataPermissionRespDTO.setTableName(dataPermissionDefineDO.getTableName());
        dataPermissionRespDTO.setIsDbTable(dataPermissionDefineDO.getIsDbTable());
        Set<String> inDataIds = new HashSet<>();
        Set<String> liekDataIds = new HashSet<>();
        for (DataPermissionConfigDO config : permissionConfigList) {
            //所有权限
            if (DataPermissionScopeEnum.ALL.getCode().equals(config.getDataScope())) {
                inDataIds.clear();
                dataPermissionRespDTO.setAll(true);
                break;
            }
            // 指定数据权限
            else if (Objects.equals(DataPermissionScopeEnum.CUSTOM.getCode(), config.getDataScope())) {
                inDataIds.addAll(Arrays.stream(config.getDataContent().split(",")).collect(Collectors.toSet()));
            }
            // 指定数据权限（包含下级）
            else if (Objects.equals(DataPermissionScopeEnum.CUSTOM_CONTAIN_CHILD.getCode(), config.getDataScope())) {
                liekDataIds.addAll(Arrays.stream(config.getDataContent().split(",")).collect(Collectors.toSet()));
            }
            // 所属部门权限（包含下级）
            else if (Objects.equals(DataPermissionScopeEnum.DEPT_AND_CLILD.getCode(), config.getDataScope())) {
                //查询用户所属部门
                List<String> deptFullIds = orgService.getOrgFullIdListBySystemUserId(userId, OrgKindEnum.Dept);
                liekDataIds.addAll(deptFullIds);
            }
            // 仅本人数据权限
            else if (Objects.equals(DataPermissionScopeEnum.DEPT_SELF.getCode(), config.getDataScope())) {
                List<String> psmFullIds = orgService.getOrgFullIdListBySystemUserId(userId, OrgKindEnum.Psm);
                liekDataIds.addAll(psmFullIds);
            } else {
                inDataIds.addAll(Arrays.stream(config.getDataContent().split(",")).collect(Collectors.toSet()));
            }
        }

        List<UserDataPermissionRespDTO.ConfigData> configDataList = new ArrayList<>();
        configDataList.add(new UserDataPermissionRespDTO.ConfigData(LikeOrInEnum.IN.getId(), inDataIds));
        configDataList.add(new UserDataPermissionRespDTO.ConfigData(LikeOrInEnum.LIKE.getId(), liekDataIds));
        dataPermissionRespDTO.setConfigList(configDataList);
        return dataPermissionRespDTO;
    }


    /**
     * 从redis中获取数据权限。缓存保持1分钟。
     *
     * @param userAndPermissionCode
     * @return
     */
    @Cacheable(value = RedisKeyConstants.SA_USER_PERMISSION, key = "#userAndPermissionCode")
    public UserDataPermissionRespDTO getUserDataPermissionByCode3(Map.Entry<Long, String> userAndPermissionCode) {
        return getSelf().getUserDataPermissionByCode2(userAndPermissionCode.getKey(), userAndPermissionCode.getValue());
    }

    /**
     * 从本地缓存中获取用户数据权限。
     * 从Redis获取数据权限，另见{@Link DataPermissionDetailServiceImpl#getUserDataPermissionByCodeFromRedis}
     *
     * @param userId
     * @param permissionSubCode
     * @return
     */
    @Override
    @SneakyThrows
    public UserDataPermissionRespDTO getUserDataPermissionByCode(Long userId, String permissionSubCode) {
        return userDataPermissionCache.get(new KeyValue<>(userId, permissionSubCode));
    }

    /**
     * 从redis缓存中获取数据权限，
     * 从本地缓存获取，另见{@link DataPermissionDetailServiceImpl#getUserDataPermissionByCode}
     *
     * @param userId
     * @param permissionSubCode
     * @return
     */
    @Override
    public UserDataPermissionRespDTO getUserDataPermissionByCodeFromRedis(Long userId, String permissionSubCode) {
        Map<Long, String> map = new HashMap<>();
        map.put(userId, permissionSubCode);
        return getSelf().getUserDataPermissionByCode3(map.entrySet().stream().collect(Collectors.toList()).get(0));
    }


}
